//implement timer mlcai 2011-06-13

#include "NameSpaceDef.h"
#include ADD_QUOTE(INC_NAME_HEADER(NAMESPACE_NAME, Timer.h))

BEGIN_CXX_NAMESPACE_DEFINITION

Timer::Timer() : m_schedulePool(ScheProcessPool::instance())
{
}

Timer::~Timer()
{
	canncel();
}

void Timer::start(long mseconds)
{
	m_scheduleTime = mseconds;
	m_schedulePool.putTimer(this);
}

void Timer::canncel()
{
	m_schedulePool.deleteTimer(this);
}

ScheProcessPool ScheProcessPool::_scheProFac;

ScheProcessPool::ScheProcessPool()
{
}

ScheProcessPool::~ScheProcessPool()
{
}

ScheProcessPool& ScheProcessPool::instance()
{
	return _scheProFac;
}

void ScheProcessPool::putTimer(Timer* timer)
{
	ScheduleProcessor* scheProcessor = getScheProcessor(timer->m_scheduleTime);
	scheProcessor->addTimer(timer);
}

void ScheProcessPool::deleteTimer(Timer* timer)
{
	std::pair<multimap<long, ScheduleProcessor*>::iterator, multimap<long, ScheduleProcessor*>::iterator> ret;
	long unitInt = getScheUnitInterval(timer->m_scheduleTime);
	m_scheProceMutex.lock();
	ret = m_scheProcessors.equal_range(unitInt);
	multimap<long, ScheduleProcessor*>::iterator it = ret.first;
	while (it != ret.second)
	{
		ScheduleProcessor* processor = it->second;
		if (processor->removeTimer(timer) && processor->isTasksEmpty())
		{
			processor->stop();
			delete processor;
			m_scheProcessors.erase(it);
			break;
		}
		else
			++it;
	}
	m_scheProceMutex.unlock();
}

ScheProcessPool::ScheduleProcessor::ScheduleProcessor(long waitUnitIv, long maxTaskSize) :
m_waitUnitIv(waitUnitIv), m_isRunning(false), m_isTasksFull(false), m_maxTaskSize(maxTaskSize)
{
}

ScheProcessPool::ScheduleProcessor::~ScheduleProcessor()
{
}

void ScheProcessPool::ScheduleProcessor::addTimer(Timer* timer)
{
	TimeTask task(timer->m_scheduleTime, timer);
	m_taskMutex.lock();
	if (m_timeTasks.find(timer) == m_timeTasks.end())
		m_timeTasks.insert(map<Timer*, TimeTask>::value_type(timer, task));
	m_taskMutex.unlock();
	if (!m_isRunning)
		start();
}

bool ScheProcessPool::ScheduleProcessor::removeTimer(Timer* timer)
{
	m_taskMutex.lock();
	map<Timer*, TimeTask>::iterator it;
	if ((it = m_timeTasks.find(timer)) != m_timeTasks.end())
	{
		m_timeTasks.erase(it);
		m_taskMutex.unlock();
		return true;
	}
	m_taskMutex.unlock();
	return false;
}

bool ScheProcessPool::ScheduleProcessor::isTasksFull()
{
	return m_isTasksFull;
}

bool ScheProcessPool::ScheduleProcessor::isTasksEmpty()
{
	return m_timeTasks.empty();
}

void ScheProcessPool::ScheduleProcessor::stop()
{
	m_isRunning = false;
	m_waitor.set();
	join();
}

void ScheProcessPool::ScheduleProcessor::run()
{
	m_isRunning = true;
	while (m_isRunning)
	{
		m_waitor.wait(m_waitUnitIv);
		long taskSize = 0;
		map<Timer*, TimeTask>::iterator it;
		m_taskMutex.lock();
		it = m_timeTasks.begin();
		if (it == m_timeTasks.end())
		{
			m_taskMutex.unlock();
			break;
		}
		for (; it != m_timeTasks.end(); ++it)
		{
			TimeTask& task = it->second;
			if ((task.m_curRestTime -= m_waitUnitIv) <= 0)
			{
				task.m_timer->schedule();
				task.m_curRestTime = task.m_timer->m_scheduleTime;
			}
			++taskSize;
		}
		m_taskMutex.unlock();
		m_isTasksFull = (taskSize >= m_maxTaskSize);
	}
	m_isRunning = false;
}

ScheProcessPool::ScheduleProcessor* ScheProcessPool::getScheProcessor(long interval)
{
	ScheduleProcessor* processor = 0;
	long unitInt = getScheUnitInterval(interval);
	m_scheProceMutex.lock();
	multimap<long, ScheduleProcessor*>::iterator it = m_scheProcessors.find(unitInt);
	if (it != m_scheProcessors.end() && unitInt != interval)
	{
		processor = it->second;
		if (processor->isTasksFull())
		{
			processor = new ScheduleProcessor(unitInt);
			m_scheProcessors.insert(multimap<long, ScheduleProcessor*>::value_type(unitInt, processor));
		}
	}
	else
	{
		processor = new ScheduleProcessor(unitInt);
		m_scheProcessors.insert(multimap<long, ScheduleProcessor*>::value_type(unitInt, processor));
	}
	m_scheProceMutex.unlock();
	return processor;
}

long ScheProcessPool::getScheUnitInterval(long interval)
{
	long unitInterval = interval;
	if (interval / 10 >= 1000 && interval % 1000 == 0)
		unitInterval = 1000;
	else if (interval / 10 >= 100 && interval % 100 == 0)
		unitInterval = 100;
	else if (interval / 10 >= 10 && interval % 10 == 0)
		unitInterval = 10;
	return unitInterval;
}

END_CXX_NAMESPACE_DEFINITION
